package chapter02.part03;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

/**
 * 类的版本与序列化的关系
 * 
 * 场景/问题: 随着类的改变, 指纹(serialVersionUID)也会改变, 序列化就会失败
 * 方案: 手动指定 serialVersionUID
 * 
 * 补充: 序列化的文件格式
 * 序列化的文件格式中包含 类的版本唯一的uid
 * uid: 使用 sha 算法, 对类, 父类, 接口, 字段, 方法签名按照规范排序后, 进行安全散列 * 
 * 
 * @author insight
 * @since 2021/8/17
 */
public class VersionControl {
    public static void main(String[] args) throws IOException {
        /*
         * 实验流程:
         * 先写后读
         * 序列化后修改测试对象(添加字段) 进行反序列化测试
         * 测试类路径 test.java.chapter02.part03.VersionTestObjectTest
         * 
         * afterChangeWithoutUID()
         * 预期: 反序列化失败
         * 实际: 提示 uid 不匹配
         * java.io.InvalidClassException: chapter02.part03.VersionTestObject;
         * local class incompatible:
         * stream classdesc serialVersionUID = xxx,
         * local class serialVersionUID = xxx
         * 
         * afterChangeWithUID()
         * 使用老版本的 uid
         * 预期: 成功反序列化
         * 实际: 成功
         */
        beforeChange();
        
        /*
         * 测试中修改的是方法 如果修改的是字段呢?
         * 序列化的处理方法是
         * 如果旧版本的字段更多 舍弃多出来的字段
         * 如果新版本的字段更多 设置新字段为 null
         * null 意味着不安全
         * 因此可以自定义反序列化 来兼容旧版本
         * 
         * 另外 serialVersionUID 不是一定要添加
         * 视情况而定
         * 如果短期序列化 / 不需要持久化 那就不需要手动添加
         * 因为有可能忘记修改 serialVersionUID
         * 
         * 思考:
         * 书上说的很奇怪 既然如此 直接从第一个版本就指定一个 uid 不变不就行了?
         * 或者说存在一种情况 有多个旧版本的数据 所以需要多次修改 uid ?
         */
    }

    private static void beforeChange() throws IOException {
        ObjectOutputStream out =
                new ObjectOutputStream(new FileOutputStream(
                        "chapter02-io/src/test/java/chapter02/part03/test.txt"));
        out.writeObject(new VersionTestObject("0.1"));
    }
}